import json
import os
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Union
from collections import defaultdict

# 设置中文字体（解决matplotlib中文乱码问题）
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题


class GiftDataManager:
    def __init__(self, data_file: str = "data/gift_records.json"):
        self.data_file = data_file
        self._init_data_file()  # 初始化数据文件目录
        self._init_visual_dir()  # 初始化可视化结果保存目录
        self.records: List[Dict] = self._load_records()

    def _init_data_file(self) -> None:
        """初始化数据文件目录（若不存在则创建）"""
        os.makedirs(os.path.dirname(self.data_file), exist_ok=True)
        if not os.path.exists(self.data_file):
            with open(self.data_file, 'w', encoding='utf-8') as f:
                json.dump([], f, ensure_ascii=False, indent=2)

    def _init_visual_dir(self) -> None:
        """初始化可视化图表保存目录（默认：visualization/）"""
        self.visual_dir = "visualization"
        os.makedirs(self.visual_dir, exist_ok=True)

    def _load_records(self) -> List[Dict]:
        """从JSON文件加载记录（兼容空文件或格式错误）"""
        try:
            with open(self.data_file, 'r', encoding='utf-8') as f:
                records = json.load(f)
                # 校验记录格式（确保包含必要字段）
                required_fields = ["uid", "cost", "income", "timestamp", "listen_room_id", "uname"]
                return [r for r in records if all(field in r for field in required_fields)]
        except (json.JSONDecodeError, FileNotFoundError):
            return []

    def _save_records(self) -> None:
        """保存记录到JSON文件"""
        with open(self.data_file, 'w', encoding='utf-8') as f:
            json.dump(self.records, f, ensure_ascii=False, indent=2)

    def _parse_timestamp(self, timestamp_str: str) -> Optional[datetime]:
        """辅助方法：将字符串时间戳转为datetime对象（支持格式：%Y-%m-%d %H:%M:%S）"""
        try:
            return datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S")
        except ValueError:
            print(f"无效时间格式：{timestamp_str}（需符合%Y-%m-%d %H:%M:%S）")
            return None

    def query_by_uid(self, uid: str) -> List[Dict]:
        """根据用户ID查询记录"""
        self.records = self._load_records()
        return [r for r in self.records if str(r.get("uid")) == str(uid)]

    def query_by_uname(self, uname: str) -> List[Dict]:
        """根据用户名查询记录（模糊匹配）"""
        self.records = self._load_records()
        return [r for r in self.records if uname.lower() in str(r.get("uname", "")).lower()]

    def _filter_records(
            self,
            uid: Optional[str] = None,
            uname: Optional[str] = None,  # 新增：用户名筛选
            room_id: Optional[str] = None,
            start_date: Optional[str] = None,
            end_date: Optional[str] = None
    ) -> List[Dict]:
        """辅助方法：按UID、用户名、直播间ID、时间范围筛选记录"""
        filtered = self.records.copy()

        # 按UID筛选
        if uid:
            filtered = [r for r in filtered if str(r["uid"]) == str(uid)]

        # 新增：按用户名筛选（模糊匹配）
        if uname:
            filtered = [r for r in filtered if uname.lower() in str(r.get("uname", "")).lower()]

        # 按直播间ID筛选
        if room_id:
            filtered = [r for r in filtered if str(r["listen_room_id"]) == str(room_id)]

        # 按时间范围筛选（start_date <= timestamp <= end_date）
        if start_date or end_date:
            start_dt = self._parse_timestamp(f"{start_date} 00:00:00") if start_date else None
            end_dt = self._parse_timestamp(f"{end_date} 23:59:59") if end_date else None
            filtered = [
                r for r in filtered
                if (dt := self._parse_timestamp(r["timestamp"]))
                   and (not start_dt or dt >= start_dt)
                   and (not end_dt or dt <= end_dt)
            ]

        return filtered

    def add_record(self, record: Dict) -> None:
        """添加新记录（自动补全时间戳）"""
        record_with_time = {
            **record,
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
        self.records.append(record_with_time)
        self._save_records()

    def get_total_stats(
            self,
            uid: Optional[str] = None,
            uname: Optional[str] = None,  # 新增：支持用户名查询
            room_id: Optional[str] = None
    ) -> Dict:
        """
        获取全局/指定UID/指定用户名/指定直播间的统计数据
        支持组合筛选：记录数、总花费、总收入、总盈亏
        """
        # 使用更新后的筛选方法，支持用户名筛选
        target_records = self._filter_records(uid=uid, uname=uname, room_id=room_id)

        total_cost = sum(r["cost"] for r in target_records)
        total_income = sum(r["income"] for r in target_records)
        total_profit = total_income - total_cost

        # 构建统计范围描述
        scope_parts = []
        if uid:
            scope_parts.append(f"UID:{uid}")
        if uname:
            scope_parts.append(f"用户名:{uname}")
        if room_id:
            scope_parts.append(f"直播间:{room_id}")
        if not scope_parts:
            scope_parts.append("全局")
        stat_scope = "-".join(scope_parts)

        return {
            "统计范围": stat_scope,
            "记录总数": len(target_records),
            "总花费(gold)": total_cost,
            "总收入(gold)": total_income,
            "总盈亏(gold)": total_profit,
            "平均每笔盈亏(gold)": round(total_profit / len(target_records), 2) if target_records else 0
        }

    def get_total_month_stats(self, uid: Optional[str] = None, room_id: Optional[str] = None) -> Dict:
        """查询当前月份下的收支统计"""
        today = datetime.today()
        current_year = today.year
        current_month = today.month

        # 计算当前月份的时间范围
        first_day = datetime(current_year, current_month, 1)
        if current_month == 12:
            next_month = 1
            next_year = current_year + 1
        else:
            next_month = current_month + 1
            next_year = current_year
        last_day = datetime(next_year, next_month, 1) - timedelta(seconds=1)

        start_date = first_day.strftime("%Y-%m-%d")
        end_date = last_day.strftime("%Y-%m-%d")

        # 筛选当前月份的记录
        target_records = self._filter_records(
            uid=uid,
            room_id=room_id,
            start_date=start_date,
            end_date=end_date
        )

        total_cost = sum(r["cost"] for r in target_records)
        total_income = sum(r["income"] for r in target_records)
        total_profit = total_income - total_cost
        avg_profit_per_record = round(total_profit / len(target_records), 2) if target_records else 0

        scope_parts = [f"{current_year}年{current_month}月"]
        if room_id:
            scope_parts.append(f"直播间:{room_id}")
        if uid:
            scope_parts.append(f"UID:{uid}")
        stat_scope = "-".join(scope_parts)

        return {
            "统计范围": stat_scope,
            "记录总数": len(target_records),
            "总盈亏(gold)": total_profit,
            "平均每笔盈亏(gold)": avg_profit_per_record
        }

    def daily_summary(self, date: str, uid: Optional[str] = None, uname: Optional[str] = None,
                      room_id: Optional[str] = None) -> Dict:
        """获取指定日期的总结（格式：YYYY-MM-DD）"""
        daily_records = self._filter_records(uid=uid, uname=uname, room_id=room_id, start_date=date, end_date=date)
        return self.get_total_stats(uid=uid, uname=uname, room_id=room_id)

    # 以下方法保持不变，但已确保兼容用户名查询功能
    def weekly_summary(self, year_week: str, uid: Optional[str] = None, room_id: Optional[str] = None) -> Dict:
        try:
            year, week = year_week.split("-")
            year = int(year)
            week = int(week)
        except ValueError:
            raise ValueError("周格式错误！需为YYYY-WW（如2025-37）")

        weekly_records = []
        for r in self._filter_records(uid=uid, room_id=room_id):
            if (dt := self._parse_timestamp(r["timestamp"])) and dt.year == year and dt.isocalendar()[1] == week:
                weekly_records.append(r)

        total_cost = sum(r["cost"] for r in weekly_records)
        total_income = sum(r["income"] for r in weekly_records)
        total_profit = total_income - total_cost

        return {
            "统计范围": f"UID:{uid}" if uid else f"直播间:{room_id}" if room_id else "全局",
            "统计周": f"{year}年第{week}周",
            "记录总数": len(weekly_records),
            "总花费(gold)": total_cost,
            "总收入(gold)": total_income,
            "总盈亏(gold)": total_profit
        }

    def monthly_summary(self, year_month: str, uid: Optional[str] = None, room_id: Optional[str] = None) -> Dict:
        try:
            year, month = year_month.split("-")
            year = int(year)
            month = int(month)
        except ValueError:
            raise ValueError("月格式错误！需为YYYY-MM（如2025-09）")

        monthly_records = []
        for r in self._filter_records(uid=uid, room_id=room_id):
            if (dt := self._parse_timestamp(r["timestamp"])) and dt.year == year and dt.month == month:
                monthly_records.append(r)

        total_cost = sum(r["cost"] for r in monthly_records)
        total_income = sum(r["income"] for r in monthly_records)
        total_profit = total_income - total_cost

        return {
            "统计范围": f"UID:{uid}" if uid else f"直播间:{room_id}" if room_id else "全局",
            "统计月": f"{year}年{month}月",
            "记录总数": len(monthly_records),
            "总花费(gold)": total_cost,
            "总收入(gold)": total_income,
            "总盈亏(gold)": total_profit
        }

    def room_summary(self, room_id: Optional[str] = None) -> Dict:
        room_groups = defaultdict(list)
        for r in self.records:
            room_groups[str(r["listen_room_id"])].append(r)

        if room_id:
            room_id = str(room_id)
            if room_id not in room_groups:
                return {"提示": f"直播间{room_id}无记录"}
            records = room_groups[room_id]
            return {
                "直播间ID": room_id,
                "参与用户数": len(set(str(r["uid"]) for r in records)),
                "记录总数": len(records),
                "总花费(gold)": sum(r["cost"] for r in records),
                "总收入(gold)": sum(r["income"] for r in records),
                "总盈亏(gold)": sum(r["income"] - r["cost"] for r in records),
                "热门盲盒类型": max(set(r["original_gift_name"] for r in records),
                                    key=lambda x: sum(1 for _ in records if _.get("original_gift_name") == x))
            }

        all_room_stats = []
        for rid, records in room_groups.items():
            all_room_stats.append({
                "直播间ID": rid,
                "参与用户数": len(set(str(r["uid"]) for r in records)),
                "记录总数": len(records),
                "总花费(gold)": sum(r["cost"] for r in records),
                "总收入(gold)": sum(r["income"] for r in records),
                "总盈亏(gold)": sum(r["income"] - r["cost"] for r in records)
            })
        return all_room_stats

    def uid_time_summary(self, uid: str, time_type: str = "day", time_value: str = "") -> Dict:
        if time_type == "day":
            if not time_value:
                raise ValueError("查询日总结需指定time_value（格式：YYYY-MM-DD）")
            return self.daily_summary(date=time_value, uid=uid)
        elif time_type == "week":
            if not time_value:
                raise ValueError("查询周总结需指定time_value（格式：YYYY-WW）")
            return self.weekly_summary(year_week=time_value, uid=uid)
        elif time_type == "month":
            if not time_value:
                raise ValueError("查询月总结需指定time_value（格式：YYYY-MM）")
            return self.monthly_summary(year_month=time_value, uid=uid)
        else:
            raise ValueError("time_type仅支持day/week/month")

    def sort_users(
            self,
            sort_key: str = "total_cost",
            ascending: bool = False,
            top_n: Optional[int] = None
    ) -> List[Dict]:
        user_groups = defaultdict(list)
        for r in self.records:
            user_groups[str(r["uid"])].append(r)

        user_stats = []
        for uid, records in user_groups.items():
            total_cost = sum(r["cost"] for r in records)
            total_profit = sum(r["income"] - r["cost"] for r in records)
            user_stats.append({
                "uid": uid,
                "uname": records[0]["uname"],
                "record_count": len(records),
                "total_cost": total_cost,
                "total_income": sum(r["income"] for r in records),
                "total_profit": total_profit
            })

        valid_keys = ["total_cost", "total_profit", "record_count"]
        if sort_key not in valid_keys:
            raise ValueError(f"sort_key仅支持{valid_keys}")

        sorted_users = sorted(user_stats, key=lambda x: x[sort_key], ascending=ascending)
        return sorted_users[:top_n] if top_n else sorted_users

    def plot_daily_trend(
            self,
            start_date: str,
            end_date: str,
            uid: Optional[str] = None,
            uname: Optional[str] = None,  # 支持按用户名筛选绘图
            room_id: Optional[str] = None,
            save_name: str = "daily_trend.png"
    ) -> None:
        records = self._filter_records(uid=uid, uname=uname, room_id=room_id, start_date=start_date, end_date=end_date)
        if not records:
            print("无符合条件的记录，无法生成趋势图")
            return

        daily_data = defaultdict(lambda: {"cost": 0, "income": 0, "profit": 0})
        for r in records:
            date_str = r["timestamp"].split(" ")[0]
            daily_data[date_str]["cost"] += r["cost"]
            daily_data[date_str]["income"] += r["income"]
            daily_data[date_str]["profit"] += (r["income"] - r["cost"])

        dates = sorted(daily_data.keys())
        costs = [daily_data[d]["cost"] for d in dates]
        incomes = [daily_data[d]["income"] for d in dates]
        profits = [daily_data[d]["profit"] for d in dates]

        fig, ax = plt.subplots(figsize=(12, 6))
        ax.plot(dates, costs, label="每日花费", marker="o", color="#ff6b6b")
        ax.plot(dates, incomes, label="每日收入", marker="s", color="#4ecdc4")
        ax.plot(dates, profits, label="每日盈亏", marker="^", color="#45b7d1")

        title_parts = []
        if uid:
            title_parts.append(f"用户{uid}")
        if uname:
            title_parts.append(f"用户名{uname}")
        if room_id:
            title_parts.append(f"直播间{room_id}")
        title_suffix = "-".join(title_parts) + "/" if title_parts else ""

        ax.set_title(f"{title_suffix}收支趋势图（{start_date}至{end_date}）", fontsize=14)
        ax.set_xlabel("日期", fontsize=12)
        ax.set_ylabel("金额（gold）", fontsize=12)
        ax.legend(fontsize=10)
        ax.grid(True, alpha=0.3)
        plt.xticks(rotation=45)
        plt.tight_layout()

        save_path = os.path.join(self.visual_dir, save_name)
        plt.savefig(save_path, dpi=300, bbox_inches="tight")
        plt.close()
        print(f"日收支趋势图已保存至：{save_path}")

    def plot_room_profit(self, save_name: str = "room_profit.png") -> None:
        room_stats = self.room_summary()
        if not room_stats:
            print("无直播间记录，无法生成对比图")
            return

        room_stats_sorted = sorted(room_stats, key=lambda x: x["总盈亏(gold)"], reverse=True)
        room_ids = [r["直播间ID"] for r in room_stats_sorted]
        profits = [r["总盈亏(gold)"] for r in room_stats_sorted]

        colors = ["#2ecc71" if p >= 0 else "#e74c3c" for p in profits]
        fig, ax = plt.subplots(figsize=(10, 6))
        bars = ax.bar(room_ids, profits, color=colors, alpha=0.8)

        ax.set_title("各直播间总盈亏对比", fontsize=14)
        ax.set_xlabel("直播间ID", fontsize=12)
        ax.set_ylabel("总盈亏（gold）", fontsize=12)
        ax.grid(True, alpha=0.3, axis="y")

        for bar, profit in zip(bars, profits):
            height = bar.get_height()
            ax.text(
                bar.get_x() + bar.get_width() / 2.,
                height + (500 if height >= 0 else -1500),
                f"{profit:,}",
                ha="center", va="bottom" if height >= 0 else "top",
                fontsize=9
            )

        plt.tight_layout()
        save_path = os.path.join(self.visual_dir, save_name)
        plt.savefig(save_path, dpi=300, bbox_inches="tight")
        plt.close()
        print(f"直播间盈亏对比图已保存至：{save_path}")

    def plot_user_spending_ranking(self, top_n: int = 10, save_name: str = "user_spending_ranking.png") -> None:
        top_users = self.sort_users(sort_key="total_cost", ascending=False, top_n=top_n)
        if not top_users:
            print("无用户记录，无法生成排名图")
            return

        user_labels = [f"{u['uname']}\n(UID:{u['uid']})" for u in top_users]
        user_costs = [u["total_cost"] for u in top_users]

        fig, ax = plt.subplots(figsize=(10, 0.8 * top_n))
        bars = ax.barh(range(len(user_labels)), user_costs, color="#3498db", alpha=0.8)

        ax.set_title(f"用户总花费TOP {top_n} 排名", fontsize=14)
        ax.set_xlabel("总花费（gold）", fontsize=12)
        ax.set_yticks(range(len(user_labels)))
        ax.set_yticklabels(user_labels, fontsize=10)
        ax.grid(True, alpha=0.3, axis="x")

        for i, (bar, cost) in enumerate(zip(bars, user_costs)):
            ax.text(
                cost + 1000,
                bar.get_y() + bar.get_height() / 2.,
                f"{cost:,}",
                ha="left", va="center",
                fontsize=9
            )

        plt.tight_layout()
        save_path = os.path.join(self.visual_dir, save_name)
        plt.savefig(save_path, dpi=300, bbox_inches="tight")
        plt.close()
        print(f"用户花费TOP{top_n}排名图已保存至：{save_path}")
